package jmind.dolphin.redis.server.handler;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;
import jmind.dolphin.redis.Command;
import jmind.dolphin.redis.RedisException;
import jmind.dolphin.redis.client.handler.RedisReplyDecoder;


import java.io.IOException;
import java.util.List;



/**
 * Decode commands.
 * Redis的通讯协议可以说大集汇了……消息头标识，消息行还有就行里可能还有个数据块大小描述.首先Redis是以行来划分，每行以\r\n行结束。每一行都有一个消息头，消息头共分为5种分别如下:

 (+) 表示一个正确的状态信息，具体信息是当前行+后面的字符。

 (-)  表示一个错误信息，具体信息是当前行－后面的字符。

 (*) 表示消息体总共有多少行，不包括当前行,*后面是具体的行数。

 ($) 表示下一行数据长度，不包括换行符长度\r\n,$后面则是对应的长度的数据。

 (:) 表示返回一个数值，：后面是相应的数字节符。

 以上就是Redis协议的基础组成部分，下面来分析几个指令了解一下具体相关指令和返回情况.
 */
public class RedisCommandDecoder extends ReplayingDecoder<Void> {

  private byte[][] bytes;
  private int arguments = 0;

  @Override
  protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
    if (bytes != null) {
      int numArgs = bytes.length;
      for (int i = arguments; i < numArgs; i++) {
        if (in.readByte() == '$') {
          long l = RedisReplyDecoder.readLong(in);
          if (l > Integer.MAX_VALUE) {
            throw new IllegalArgumentException("Java only supports arrays up to " + Integer.MAX_VALUE + " in size");
          }
          int size = (int) l;
          bytes[i] = new byte[size];
          in.readBytes(bytes[i]);
          if (in.bytesBefore((byte) '\r') != 0) {
            throw new RedisException("Argument doesn't end in CRLF");
          }
          in.skipBytes(2);
          arguments++;
          checkpoint();
        } else {
          throw new IOException("Unexpected character");
        }
      }
      try {
        out.add(new Command(bytes));
      } finally {
        bytes = null;
        arguments = 0;
      }
    } else if (in.readByte() == '*') {
      long l = RedisReplyDecoder.readLong(in);
      if (l > Integer.MAX_VALUE) {
        throw new IllegalArgumentException("Java only supports arrays up to " + Integer.MAX_VALUE + " in size");
      }
      int numArgs = (int) l;
      if (numArgs < 0) {
        throw new RedisException("Invalid size: " + numArgs);
      }
      bytes = new byte[numArgs][];
      checkpoint();
      decode(ctx, in, out);
    } else {
      // Go backwards one
      in.readerIndex(in.readerIndex() - 1);
      // Read command -- can't be interupted
      byte[][] b = new byte[1][];
      b[0] = in.readBytes(in.bytesBefore((byte) '\r')).array();
      in.skipBytes(2);
      out.add(new Command(b, true));
    }
  }

}
